package de.fhin.mas.agents.manager.behaviours;

import jade.core.behaviours.Behaviour;
import jade.core.behaviours.FSMBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.domain.FIPANames;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import de.fhin.mas.MASEvents;
import de.fhin.mas.agents.manager.ManagerAgent;
import de.fhin.mas.util.MessageUtil;

/**
 * System-Stop (Pause) auslösen.
 */
public class InitSystemStop extends FSMBehaviour {
	private static final long serialVersionUID = 1043677633374116835L;

	private final ManagerAgent managerAgent;
	private String reasonForStop;

	private class SendStopCallBv extends OneShotBehaviour {
		private static final long serialVersionUID = -246038986864750967L;

		@Override
		public void action() {
			// TODO: Für Message eine Onthologie verwenden
			String Content = reasonForStop;

			ACLMessage systemStop = new ACLMessage(ACLMessage.REQUEST);
			systemStop.setProtocol(FIPANames.InteractionProtocol.FIPA_REQUEST);
			systemStop.setContent(Content);
			MessageUtil.setMessageTopic(managerAgent, systemStop, MASEvents.SYSTEM_STOP_REQUIRED);

			// Abschicken der Nachricht
			managerAgent.sendToAll(systemStop);
		}

		@Override
		public int onEnd() {
			return 0;
		}

	}

	private class WaitForAgreesBv extends Behaviour {
		private static final long serialVersionUID = 6061332358770279387L;

		private int receivedAgrees = 0;
		private Integer doTransition = null;

		// private final Date startTime = new Date();

		@Override
		public void action() {
			MessageTemplate mt = MessageTemplate.and(MessageTemplate.MatchConversationId(MASEvents.SYSTEM_STOP_REQUIRED
					.getName()), MessageTemplate.or(MessageTemplate.MatchPerformative(ACLMessage.AGREE),
					MessageTemplate.MatchPerformative(ACLMessage.REFUSE)));

			managerAgent.logInfo("system_stop_req: Waiting for PROPOSE or REFUSE or a timeout!");

			// für Testzwecke ohne Timeout!
			/*
			 * if (new Date().getTime() - startTime.getTime() > 20000) {
			 * doTransition = 0; System.out.println("Timeout"); }
			 */

			ACLMessage msg = myAgent.receive(mt);
			if (msg == null)
				block();
			else {
				if (msg.getPerformative() == ACLMessage.AGREE) {
					receivedAgrees++;
					managerAgent.logInfo("system_stop_req: SystemStop agree received");
					if (receivedAgrees >= 3)
						doTransition = 1;
				} else if (msg.getPerformative() == ACLMessage.REFUSE)
					doTransition = 0;
			}
		}

		@Override
		public boolean done() {
			return doTransition != null;
		}

		@Override
		public int onEnd() {
			if (doTransition == 0)
				managerAgent.logInfo("system_stop_req: Transition to stop_disagreed");

			else if (doTransition == 1)
				managerAgent.logInfo("system_stop_req: Transition to stop_agreed");

			return doTransition;
		}
	}

	private class StopAgreedBv extends OneShotBehaviour {
		private static final long serialVersionUID = -3272167831720915408L;

		@Override
		public void action() {
			managerAgent.logInfo("system_stop_req: State stop_agreed reached.");

			// Nach erfolgreichem Stop und Ausgabe an GUI muss noch ein
			// INFORM-DONE abgeschickt werden
			String Content = "DONE";

			// "INFORM-DONE" Message = INFORM Message
			ACLMessage systemStopDone = new ACLMessage(ACLMessage.INFORM);
			systemStopDone.setProtocol(FIPANames.InteractionProtocol.FIPA_REQUEST);
			systemStopDone.setContent(Content);
			MessageUtil.setMessageTopic(managerAgent, systemStopDone, MASEvents.SYSTEM_STOP_REQUIRED);

			// Abschicken der Nachricht
			managerAgent.sendToAll(systemStopDone);
		}

		@Override
		public int onEnd() {
			return super.onEnd();
		}
	}

	private class StopDisagreedBv extends OneShotBehaviour {
		private static final long serialVersionUID = 4583474584983422511L;

		@Override
		public void action() {
			managerAgent.logInfo("system_stop_req: State stop_disagreed reached.");
		}
	}

	public InitSystemStop(ManagerAgent a, Object reason) {
		super(a);

		this.managerAgent = a;
		this.reasonForStop = reason.toString();

		registerFirstState(new SendStopCallBv(), "stop_init");
		registerState(new WaitForAgreesBv(), "wait_for_agrees");
		registerLastState(new StopDisagreedBv(), "stop_disagreed");
		registerLastState(new StopAgreedBv(), "stop_executed");

		registerTransition("stop_init", "wait_for_agrees", 0);
		registerTransition("wait_for_agrees", "stop_disagreed", 0);
		registerTransition("wait_for_agrees", "stop_executed", 1);
	}

}
